package com.jyj;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

/**
 * @class: com.jyj.ShiroBasic
 * @description: 测试shiro的基本属性，
 * SecurityManager->Realm->SecurityUtils->Subject
 * @author: jiangzengkui
 * @company: 教育家
 * @create: 2021-01-03 21:07
 */
public class ShiroBasic {
    /**
     * 基本登陆验证
     * SimpleAccountRealm
     */
    @Test
    public void loginTest(){
        //1.创建一个SecurityManager类
        DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
        //创建一个realm域，存储用户账号信息
        SimpleAccountRealm simpleAccountRealm=new SimpleAccountRealm();
        simpleAccountRealm.addAccount("jzk","1234");
        //设置到SecurityManager
        defaultSecurityManager.setRealm(simpleAccountRealm);
        //2.创建一个主体
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject=SecurityUtils.getSubject();
        //3.创建一个登陆token
        UsernamePasswordToken token=new UsernamePasswordToken("jzk","1234");
        //登陆
        try{
            subject.login(token);
            //查看登录是否成功
            System.out.println("login is "+subject.isAuthenticated());
        }
        catch (UnknownAccountException ex){
            System.out.println("登录账号出错");
        }
        catch (IncorrectCredentialsException e){
            System.out.println("密码错误");
        }
        catch (Exception e){
            System.out.println("其他异常");
        }

    }

    /**
     * 验证角色权限SimpleAccountRealm
     */
    @Test
    public void roleValidare(){
     //1.创建一个SecurityManager类
     DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
     //创建一个realm域，存储用户账号信息
     SimpleAccountRealm simpleAccountRealm=new SimpleAccountRealm();
     //角色组
     String roles[]=new String[]{"admin","approve","user"};
     simpleAccountRealm.addAccount("jzk","1234",roles);
     //设置到SecurityManager
     defaultSecurityManager.setRealm(simpleAccountRealm);
     //2.创建一个主体
     SecurityUtils.setSecurityManager(defaultSecurityManager);
     Subject subject=SecurityUtils.getSubject();
     //3.创建一个登陆token
     UsernamePasswordToken token=new UsernamePasswordToken("jzk","1234");
     //登陆
     try{
         subject.login(token);
         //查看登录是否成功
         System.out.println("login is "+subject.isAuthenticated());

         //==============是否有相关角色
         //权限role验证
         //单个角色
         subject.checkRole("admin");//注意这个不会返回bool，而是通过UnauthorizedException异常
         System.out.println("role is "+subject.isAuthenticated());
         //多个角色
         subject.checkRoles("admin","user");
         System.out.println("roles is "+subject.isAuthenticated());




         //退出
         subject.logout();
     }
     catch (UnknownAccountException ex){
         System.out.println("登录账号出错");
     }
     catch (IncorrectCredentialsException e){
         System.out.println("密码错误");
     }
     catch (UnauthorizedException e){
         System.out.println("没有相关权限");
     }
     catch (Exception e){
         System.out.println("其他异常");
     }
    }

    /**
     * 使用simpleAccountRealm域只有演示功能，实际上是存储在资源文件里的（ini数据库）
     ==================ini文件start================
     #用户
     [users]
     #用户zhang的密码是123，此用户具有role1和role2两个角色
     zhang=123,role1,role2
     wang=123,role2

     #权限
     [roles]
     #角色role1对资源user拥有create、update权限
     role1=user:create,user:update
     #角色role2对资源user拥有create、delete权限
     role2=user:init,user:delete
     #角色role3对资源user拥有create权限
     role3=user:query
     ==================ini文件end================
     */
    @Test
    public void iniResource(){
//1.创建一个SecurityManager类
        DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
        //创建一个inirealm域，存储用户账号信息
        IniRealm iniRealm=new IniRealm("classpath:ini/user.ini");

        //设置到SecurityManager
        defaultSecurityManager.setRealm(iniRealm);
        //2.创建一个主体
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject=SecurityUtils.getSubject();
        //3.创建一个登陆token
        validate(subject);


    }

    /**
     * 从jdbcRealm领域，存储到数据库中

     SET FOREIGN_KEY_CHECKS=0;



     DROP TABLE IF EXISTS `users`;

     CREATE TABLE `users` (

     `password` varchar(255) DEFAULT NULL,

     `password_salt` varchar(255) DEFAULT NULL,

     `username` varchar(255) NOT NULL,

     PRIMARY KEY (`username`)

     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;





     -- ----------------------------

     -- Table structure for user_roles

     -- ----------------------------

     DROP TABLE IF EXISTS `user_roles`;

     CREATE TABLE `user_roles` (

     `username` varchar(255) DEFAULT NULL,

     `role_name` varchar(255) DEFAULT NULL

     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;





     -- ----------------------------

     -- Table structure for roles_permissions

     -- ----------------------------

     DROP TABLE IF EXISTS `roles_permissions`;

     CREATE TABLE `roles_permissions` (

     `role_name` varchar(255) NOT NULL,

     `permission` varchar(255) NOT NULL,

     PRIMARY KEY (`role_name`,`permission`)

     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

     */
    @Test
    public void realmFromDBDefault(){

        //1.创建一个SecurityManager类
        DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
        //创建一个jdbcRealm域，存储用户账号信息
        JdbcRealm jdbcRealm=new JdbcRealm();
        DruidDataSource ds=new DruidDataSource();
        ds.setUrl("jdbc:mysql://127.0.0.1:3306/shiro?serverTimezone=UTC");
        ds.setUsername("root");
        ds.setPassword("111111");
        jdbcRealm.setDataSource(ds);
        //权限查询开关，默认为false,不设置将查询不到权限
        jdbcRealm.setPermissionsLookupEnabled(true);
        //设置到SecurityManager
        defaultSecurityManager.setRealm(jdbcRealm);
        //2.创建一个主体
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject=SecurityUtils.getSubject();
        //3.创建一个登陆token
        validate(subject);

    }

    @Test
    /**
     * 自定义表结构，自定义SQL

     -- ----------------------------
     -- Table structure for xt_user
     -- ----------------------------
     DROP TABLE IF EXISTS `xt_user`;
     CREATE TABLE `xt_user` (
     `user_id` int(11) NOT NULL AUTO_INCREMENT,
     `user_name` varchar(255) DEFAULT NULL,
     `login_code` varchar(255) DEFAULT NULL,
     `login_pwd` varchar(255) DEFAULT NULL,
     `pwd_salt` varchar(255) DEFAULT NULL,
     PRIMARY KEY (`user_id`)
     ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

     DROP TABLE IF EXISTS `xt_role`;
     CREATE TABLE `xt_role` (
     `role_id` int(11) NOT NULL AUTO_INCREMENT,
     `role_name` varchar(255) DEFAULT NULL,
     PRIMARY KEY (`role_id`)
     ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
     DROP TABLE IF EXISTS `xt_resource`;
     CREATE TABLE `xt_resource` (
     `resource_id` int(11) NOT NULL AUTO_INCREMENT,
     `resour_name` varchar(255) DEFAULT NULL,
     PRIMARY KEY (`resource_id`)
     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

     DROP TABLE IF EXISTS `xt_user_role`;
     CREATE TABLE `xt_user_role` (
     `user_role_id` int(11) NOT NULL,
     `user_id` int(11) DEFAULT NULL,
     `role_id` int(11) DEFAULT NULL,
     PRIMARY KEY (`user_role_id`)
     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

     DROP TABLE IF EXISTS `xt_role_resource`;
     CREATE TABLE `xt_role_resource` (
     `role_res_id` int(11) NOT NULL AUTO_INCREMENT,
     `resource_id` int(11) DEFAULT NULL,
     `role_id` int(11) DEFAULT NULL,
     PRIMARY KEY (`role_res_id`)
     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

     */
    public void jdbcRelmCustTable(){
        //1.创建一个SecurityManager类
        DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
        //创建一个jdbcRealm域，存储用户账号信息
        JdbcRealm jdbcRealm=new JdbcRealm();
        DruidDataSource ds=new DruidDataSource();
        ds.setUrl("jdbc:mysql://127.0.0.1:3306/shiro?serverTimezone=UTC");
        ds.setUsername("root");
        ds.setPassword("111111");
        jdbcRealm.setDataSource(ds);
        //权限查询开关，默认为false,不设置将查询不到权限
        jdbcRealm.setPermissionsLookupEnabled(true);

        //=============设置自定义SQL========
        /** 系统自带，需要按照这个规律
         protected String authenticationQuery = "select password from users where username = ?";
         protected String userRolesQuery = "select role_name from user_roles where username = ?";
         protected String permissionsQuery = "select permission from roles_permissions where role_name = ?";
         */
        //登录sql
        String authenticationQuery="select login_pwd from xt_user where login_code=?";
        jdbcRealm.setAuthenticationQuery(authenticationQuery);

        //角色查询SQL
        String userRolesQuery="select c.role_name " +
                "from xt_user a " +
                "INNER JOIN xt_user_role b on  a.user_id=b.user_id " +
                "INNER JOIN xt_role c ON b.role_id=c.role_id " +
                " where login_code=?";
        jdbcRealm.setUserRolesQuery(userRolesQuery);

        //查询权限SQL
        String permissionsQuery="select c.resour_name FROM " +
                "xt_role a " +
                "INNER JOIN xt_role_resource b ON a.role_id=b.role_id " +
                "INNER JOIN xt_resource c ON c.resource_id=b.resource_id " +
                "where a.role_name=?";

        jdbcRealm.setPermissionsQuery(permissionsQuery);

        //设置到SecurityManager
        defaultSecurityManager.setRealm(jdbcRealm);
        //2.创建一个主体
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject=SecurityUtils.getSubject();
        //3.创建一个登陆token
        validate(subject);
    }
 @Test
 /**
  * 自定义域类
  */
    public void custRealm(){
//1.创建一个SecurityManager类
     DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
     //创建一个jdbcRealm域，存储用户账号信息
     CustRealm custRealm=new CustRealm();//自定义

     //设置到SecurityManager
     defaultSecurityManager.setRealm(custRealm);
     //2.创建一个主体
     SecurityUtils.setSecurityManager(defaultSecurityManager);
     Subject subject=SecurityUtils.getSubject();
     //3.创建一个登陆token
     validate(subject);
    }

    /**
     * 自定义加盐和不加盐的密码的sql
     */
    @Test
    public void custSQLSaltRealm(){
        //1.创建一个SecurityManager类
        DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
        //创建一个jdbcRealm域，存储用户账号信息
        JdbcRealm jdbcRealm=new JdbcRealm();
        DruidDataSource ds=new DruidDataSource();
        ds.setUrl("jdbc:mysql://127.0.0.1:3306/shiro?serverTimezone=UTC");
        ds.setUsername("root");
        ds.setPassword("111111");
        jdbcRealm.setDataSource(ds);
        //权限查询开关，默认为false,不设置将查询不到权限
        jdbcRealm.setPermissionsLookupEnabled(true);

        //=============设置自定义SQL========
        /** 系统自带，需要按照这个规律
         protected String authenticationQuery = "select password from users where username = ?";
         protected String userRolesQuery = "select role_name from user_roles where username = ?";
         protected String permissionsQuery = "select permission from roles_permissions where role_name = ?";
         */
        //登录sql
        String authenticationQuery="select login_pwd from xt_user where login_code=?";
        //加盐,需要指定加盐字段
       // authenticationQuery="select login_pwd,pwd_salt from xt_user where login_code=?";
        jdbcRealm.setAuthenticationQuery(authenticationQuery);

        //角色查询SQL
        String userRolesQuery="select c.role_name " +
                "from xt_user a " +
                "INNER JOIN xt_user_role b on  a.user_id=b.user_id " +
                "INNER JOIN xt_role c ON b.role_id=c.role_id " +
                " where login_code=?";
        jdbcRealm.setUserRolesQuery(userRolesQuery);

        //查询权限SQL
        String permissionsQuery="select c.resour_name FROM " +
                "xt_role a " +
                "INNER JOIN xt_role_resource b ON a.role_id=b.role_id " +
                "INNER JOIN xt_resource c ON c.resource_id=b.resource_id " +
                "where a.role_name=?";

        jdbcRealm.setPermissionsQuery(permissionsQuery);

        //如果密码是加密而且加盐
        HashedCredentialsMatcher matcher=new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("md5");//加密算法
        matcher.setHashIterations(1);//加密次数
        jdbcRealm.setCredentialsMatcher(matcher);
        //字段加盐
        //jdbcRealm.setSaltStyle(JdbcRealm.SaltStyle.COLUMN);



        //设置到SecurityManager
        defaultSecurityManager.setRealm(jdbcRealm);
        //2.创建一个主体
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject=SecurityUtils.getSubject();
        //3.创建一个登陆token
        validate(subject);
    }

    @Test
    /**
     * 自定义域加盐类
     */
    public void custSaltRealm(){
//1.创建一个SecurityManager类
        DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
        //创建一个jdbcRealm域，存储用户账号信息
        CustSaltRealm custRealm=new CustSaltRealm();//自定义

        //如果密码是加密
        HashedCredentialsMatcher matcher=new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("md5");//加密算法
        matcher.setHashIterations(1);//加密次数
        custRealm.setCredentialsMatcher(matcher);



        //设置到SecurityManager
        defaultSecurityManager.setRealm(custRealm);
        //2.创建一个主体
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject=SecurityUtils.getSubject();
        //3.创建一个登陆token
        validate(subject);
    }

    private void validate( Subject subject){
        //3.创建一个登陆token
        UsernamePasswordToken token=new UsernamePasswordToken("zhang","123");
        //登陆
        try{
            subject.login(token);
            //查看登录是否成功
            System.out.println("login is "+subject.isAuthenticated());


            //角色验证
            System.out.println("=====角色验证====");
            //单个角色
            subject.checkRole("role1");//注意这个不会返回bool，而是通过UnauthorizedException异常
            System.out.println("role[role1] is "+subject.isAuthenticated());
            //多个角色
            subject.checkRoles("role1","role2");
            System.out.println("roles[role1,role2]  is "+subject.isAuthenticated());

            //权限验证
            System.out.println("=====权限验证====");
            System.out.println("======subject.isAuthenticated判断权限,无抛出异常UnauthorizedException==============");
            //单个权限
            subject.checkPermission("user:create");
            System.out.println("Permission[user:create]  is "+subject.isAuthenticated());
            //多个权限
            subject.checkPermissions("user:create","user:update");
            System.out.println("Permissions[user:create,user:update]  is "+subject.isAuthenticated());


            System.out.println("======subject.isPermitted判断权限==============");
            //使用isPermission判断权限，返回boolean,boolean[]值不会抛出异常
            //单个权限
            System.out.println("Permission[user:create]  is "+subject.isPermitted("user:create"));
            //多个权限
            System.out.println("Permissions[user:create,user:update]  is "+subject.isPermittedAll("user:create","user:update"));
            //退出
            subject.logout();
        }
        catch (UnknownAccountException ex){
            System.out.println("登录账号出错");
        }
        catch (IncorrectCredentialsException e){
            System.out.println("密码错误");
        }
        catch (UnauthorizedException e){
            System.out.println("没有相关权限");
        }
        catch (Exception e){
            System.out.println("其他异常");
        }
    }



}